package com.lj.common.utils;

import cn.hutool.core.thread.lock.LockUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.lj.common.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @author luojing
 * @since 2025/8/1 15:27
 * 锁相关工具类
 */
@Slf4j
public class LockUtils extends LockUtil {

    /**
     * 分布式锁使用redisson进行实现
     */
    private static final RedissonClient redissonClient;

    static {
        redissonClient = SpringUtil.getBean(RedissonClient.class);
        if (redissonClient == null) {
            throw new CommonException("无法使用分布式锁相关api! 请引入redisson依赖!");
        }
    }

    /**
     * 通过分布式锁执行 r
     *
     * @param key key
     * @param r   获取锁后要执行的逻辑
     */
    public static void lock(String key, Runnable r) {
        lock(key, () -> {
            r.run();
            return null;
        });
    }

    /**
     * 通过分布式锁执行 r
     *
     * @param key key
     * @param r   获取锁后要执行的逻辑 r的返回值就是该方法最后的返回值
     */
    public static <R> R lock(String key, Supplier<R> r) {
        if (StrUtil.isBlank(key)) {
            return null;
        }
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            return r.get();
        } finally {
            lock.unlock();
        }
    }


    /**
     * 尝试获取分布式锁执行 r 无法获取锁返回false
     *
     * @param key key
     * @param r   获取锁后要执行的逻辑
     */
    public static boolean tryLock(String key, Runnable r) {
        if (StrUtil.isBlank(key)) {
            return false;
        }
        RLock lock = redissonClient.getLock(key);
        boolean flag = lock.tryLock();
        if (!flag) {
            return false;
        }
        try {
            r.run();
            return true;
        } finally {
            lock.unlock();
        }
    }


    /**
     * 尝试获取分布式锁执行 r 无法获取锁返回false
     *
     * @param key      key
     * @param r        获取锁后要执行的逻辑
     * @param waitTime 等待时间
     * @param unit     时间单位
     */
    public static boolean tryLock(String key, Runnable r, long waitTime, TimeUnit unit) throws InterruptedException {
        if (StrUtil.isBlank(key)) {
            return false;
        }
        RLock lock = redissonClient.getLock(key);
        boolean flag = lock.tryLock(waitTime, unit);
        if (!flag) {
            return false;
        }
        try {
            r.run();
            return true;
        } finally {
            lock.unlock();
        }
    }
}
